// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package sync

import (
	"internal/race"
	"sync/atomic"
	"unsafe"
)

// WaitGroup等待goroutine集合完成。
// 主goroutine调用Add可设置要等待的
// goroutine的数量。然后，每个goroutines 
// 运行并在完成后完成调用。同时，可以使用
// 等待来阻止，直到所有goroutine完成。
// 
// WaitGroup首次使用后不得复制。
type WaitGroup struct {
	noCopy noCopy

	// 64位值：高32位为计数器，低32位为服务员计数。
	// 64位原子操作需要64位对齐，但32位
	// 编译器无法确保这一点。因此，我们分配12个字节，然后使用
	// 其中对齐的8个字节作为状态，另外4个作为存储
	// 用于语义分析。
	state1 [3]uint32
}

// state返回指向存储在wg.state1中的state和sema字段的指针。
func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {
	if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
		return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]
	} else {
		return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]
	}
}

// Add将delta（可能为负数）添加到WaitGroup计数器。
// 如果计数器变为零，则释放等待时阻塞的所有goroutine。
// 如果计数器为负值，则添加恐慌。
// 
// 请注意，计数器为零时发生的具有正增量的调用
// 必须在等待之前发生。使用负增量的调用，或使用计数器大于零时开始的
// 正增量的调用，可能随时发生
// 调用。
// 通常这意味着要添加的调用应该在语句
// 创建要等待的goroutine或其他事件之前执行。
// 如果重新使用WaitGroup来等待多组独立的事件，则
// 必须在所有以前的等待调用返回后执行新的添加调用。
// 参见WaitGroup示例。
func (wg *WaitGroup) Add(delta int) {
	statep, semap := wg.state()
	if race.Enabled {
		_ = *statep // 触发早期零删除
		if delta < 0 {
			// 将递减与等待同步。
			race.ReleaseMerge(unsafe.Pointer(wg))
		}
		race.Disable()
		defer race.Enable()
	}
	state := atomic.AddUint64(statep, uint64(delta)<<32)
	v := int32(state >> 32)
	w := uint32(state)
	if race.Enabled && delta > 0 && v == int32(delta) {
		// 第一个增量必须与Wait同步。
		// 需要将其建模为读取，因为可能存在
		// 从0开始的多个并发wg.counter转换。
		race.Read(unsafe.Pointer(semap))
	}
	if v < 0 {
		panic("sync: negative WaitGroup counter")
	}
	if w != 0 && delta > 0 && v == int32(delta) {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	if v > 0 || w == 0 {
		return
	}
	// 当waiters>0时，此goroutine已将计数器设置为0。
	// 现在不能同时发生状态突变：
	// /-添加不能与Wait同时发生，
	// /-Wait在看到计数器==0时不会增加waiters。
	// 仍然进行廉价的健全性检查，以检测WaitGroup滥用。
	if *statep != state {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	// 将服务员计数重置为0。
	*statep = 0
	for ; w != 0; w-- {
		runtime_Semrelease(semap, false, 0)
	}
}

// Done将WaitGroup计数器递减1。
func (wg *WaitGroup) Done() {
	wg.Add(-1)
}

// 等待块，直到WaitGroup计数器为零。
func (wg *WaitGroup) Wait() {
	statep, semap := wg.state()
	if race.Enabled {
		_ = *statep // 触发器nil-deref-early 
		race.Disable()
	}
	for {
		state := atomic.LoadUint64(statep)
		v := int32(state >> 32)
		w := uint32(state)
		if v == 0 {
			// 计数器为0，无需等待。
			if race.Enabled {
				race.Enable()
				race.Acquire(unsafe.Pointer(wg))
			}
			return
		}
		// 递增等待计数。
		if atomic.CompareAndSwapUint64(statep, state, state+1) {
			if race.Enabled && w == 0 {
				// 等待必须与第一次添加同步。
				// 需要将此建模为写入与读取添加的竞争。
				// 因此，只能为第一个服务员写，否则并发等待将相互竞争。
				race.Write(unsafe.Pointer(semap))
			}
			runtime_Semacquire(semap)
			if *statep != 0 {
				panic("sync: WaitGroup is reused before previous Wait has returned")
			}
			if race.Enabled {
				race.Enable()
				race.Acquire(unsafe.Pointer(wg))
			}
			return
		}
	}
}
